Self-balancing BST [AVL tree]ΒΆ

AVL tree (Adelson-Velsky and Landis) is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property.

Lookup, insertion, and deletion all take O(logN) time in both the average and worst cases, where N is the number of nodes in the tree prior to the operation. Insertions and deletions may require the tree to be rebalanced by one or more tree rotations. AVL trees are height-balanced.

[1]:
class AVLNode(object):

    def __init__(self, data, parentNode):
        self.data = data
        self.parentNode = parentNode
        self.left = None
        self.right = None
        self.balance = 0

    def insertAVL(self, data, parentNode):
        if data < self.data:
            if not self.left:
                self.left = AVLNode(data, parentNode)
            else:
                self.left.insertAVL(data, parentNode)
        else:
            if not self.right:
                self.right = AVLNode(data, parentNode)
            else:
                self.right.insertAVL(data, parentNode)
        # to check 1<balance<-1 -> make rotation
        return parentNode


    def traverseInOrderAVL(self):
        if self.left is not None:
            self.left.traverseInOrderAVL()
        print(self.data)
        if self.right is not None:
            self.right.traverseInOrderAVL()


    def getMinAVL(self):
        if self.left is None:
            return self.data
        else:
            return self.left.getMinAVL()

    def getMaxAVL(self):
        if self.right is None:
            return self.data
        else:
            return self.right.getMaxAVL()
[10]:
class BalancedTree(object):

    def __init__(self):
        self.root = None

    def insert(self, data):

        parentNode = self.root

        if self.root == None:
            parentNode = AVLNode(data, None)
            self.root = parentNode
        else:
            parentNode = self.root.insertAVL(data, self.root)

        self.rebalanceTree(parentNode)

    def height(self, node):
        if node is None:
            return -1
        else:
            return max(self.height(node.left), self.height(node.right))

    def rebalanceTree(self, parentNode):

        self.setBalance(parentNode)

        # 4 situations with rotations
        if parentNode.balance <= -1:
            if self.height(parentNode.left.left) >= self.height(parentNode.left.right):
                parentNode = self.rotateRight(parentNode)
            else:
                parentNode = self.rotateLeftRight(parentNode)
        elif parentNode.balance > 1:
            if self.height(parentNode.right.right) >= self.height(parentNode.right.left):
                parentNode = self.rotateLeft(parentNode)
            else:
                parentNode = self.rotateRightLeft(parentNode)

        if parentNode.parentNode is not None:
            self.rebalanceTree(parentNode.parentNode)
        else:
            self.root = parentNode

    def rotateLeftRight(self, node):
        print("Rotation Left-Right...")
        node.left = self.rotateLeft(node.left)
        return self.rotateRight(node)

    def rotateRightLeft(self, node):
        print("Rotation Right-Left...")
        node.right = self.rotateRight(node.right)
        return self.rotateLeft(node)

    def setBalance(self, node):
        node.balance = self.height(node.right) - self.height(node.left)

    def rotateLeft(self, node):
        print("Rotation Left...")

        tmp = node.right
        tmp.parentNode = node.parentNode
        node.right = tmp.left

        if node.right is not None:
            node.right.parentNode = node

        tmp.left = node
        node.parentNode = tmp

        if tmp.parent is not None:
            if tmp.parentNode.right == node:
                tmp.parentNode.right = tmp
            else:
                tmp.parentNode.left = tmp

        self.setBalance(node)
        self.setBalance(tmp)   # after rotation it could be unbalanced

        return tmp

    def rotateRight(self, node):
        print("Rotation Right...")

        tmp = node.left
        tmp.parentNode = node.parentNode
        node.left = tmp.right

        if node.left is not None:
            node.left.parentNode = node

        tmp.right = node
        node.parentNode = tmp

        if tmp.parentNode is not None:
            if tmp.parentNode.left == node:
                tmp.parentNode.left = tmp
            else:
                tmp.parentNode.right = tmp

        self.setBalance(node)
        self.setBalance(tmp)   # after rotation it could be unbalanced

        return tmp
[11]:
tree = BalancedTree()
# righ-left rotation
tree.insert(4)    # root
tree.insert(6)    # right   (6 > 4)
tree.insert(5)    # right.left (5 < 6)

root = tree.root
assert root.data == 4
assert root.right.data == 6
assert root.right.left.data == 5

rr = tree.rotateRight(root.right)

assert rr.data == 5
assert rr.right.data == 6
assert rr.parentNode.data == 4
Rotation Right...